package taskrunner

// Runner 属性
type Runner struct {
	Controller  controlChan // 控制管道
	Error       controlChan // 错误通道
	Data        dataChan    // 数据通道
	dataSize    int         // 数据大小
	longLived   bool        // 是否回收，true 表示不回收， false 表示回收
	Dispathcher fn          // 开始准备执行任务，也就是可以做一些在真正开始执行任务的一些初始胡操作
	Executor    fn          // 执行的具体任务, 也就是真正开始执行的任务
}

// NewRunner 创建Runner
func NewRunner(size int, longlived bool, d fn, e fn) *Runner {
	return &Runner{
		Controller:  make(chan string, 1), // 带缓冲区的表示非堵塞管道
		Error:       make(chan string, 1),
		Data:        make(chan interface{}, size),
		longLived:   longlived,
		dataSize:    size,
		Dispathcher: d,
		Executor:    e,
	}
}

// startDispatch 开始执行任务
func (r *Runner) startDispatch() {

	// 如果想要让匿名函数即时执行, 只需要在匿名函数末尾加上一个  "()" 括号即可，表示调用， 末尾不加上括号表示，不是即时调用，
	// defar 关键字表示函数快结束了的时候调用，
	// 所以的下面的匿名函数就是 在 startDispatch() 函数快结束了的时候会调用一个，然后在开始执行这个 startDispatch() 函数的时候会调用一次这个匿名函数
	//
	// 括号里面有参数表示传参:
	//
	//  defer func(a int) {
	//   	if !r.longLived {
	//   		close(r.Controller)
	//   		close(r.Data)
	//   		close(r.Error)
	//   	}
	//  }(1)

	// 申明一个匿名函数
	defer func() {
		if !r.longLived {
			// 关闭流通道
			close(r.Controller)
			close(r.Data)
			close(r.Error)
		}
	}() // 加上这个括号表示，即时(立即)执行

	for {
		// 堵塞
		// 如果任意某个通信可以进行，它就执行，其他被忽略。
		// 如果有多个 case 都可以运行，Select 会随机公平地选出一个执行。其他不会执行。
		select {
		case c := <-r.Controller:
			// 如果接收到的消息是开始执行(也就是准备阶段)
			if c == READY_TO_DISPATCH {
				// 开始执行的，开始执行的任务
				err := r.Dispathcher(r.Data)
				if err != nil {
					// 如果有错误，就发送关闭的消息到管道
					r.Error <- CLOSE
				} else {
					// 到了这里，就表示 r.Dispathcher 里面已经把任务的一些准备工作完成了
					// 开始真正执行任务，就发送执行任务的消息到管道
					r.Controller <- READY_TO_EXECUTE
				}
			}

			// 如果消息是 执行任务的消息(也就是真正开始执行)
			if c == READY_TO_EXECUTE {
				err := r.Executor(r.Data)
				if err != nil {
					// 如果有错误，就发送关闭的消息到管道
					r.Error <- CLOSE
				} else {
					// 到了这里，就表示 r.Executor 里面已经把任务做完了
					// 发消息告诉说，可以准备在执行任务
					r.Controller <- READY_TO_DISPATCH
				}
			}
		case e := <-r.Error:
			if e == CLOSE {
				return
			}

		default: // 如果上面的 case 都没有数据，就执行这里

		}
	}
}

// 启动所有任务
func (r *Runner) startAll() {
	// 先给管道发送一条消息，才能执行
	r.Controller <- READY_TO_DISPATCH
	r.startDispatch()
}
